﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Annotations.Storage;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using HeritageAcademy.Models.DataModels;
using HeritageAcademy.Models.Loaders;

namespace HeritageAcademy.Models.ViewModels
{
    public class CourseViewModel : ViewModelBase<Course>
    {
        #region Parameters 
        
        private Dictionary<ChapterViewModel, XmlStreamStore> _annotationStores;
        private ObservableCollection<CourseLocation> _bookmarks;
        private ChapterViewModelCollection _chapters;
        private CourseLocation _currentLocation;
        private List<FlowDocument> _flatChapterDocuments;
        private ChapterViewModelCollection _flatChapters;
        private bool _isFiltered;
        private bool _isHighlighted;
        private ICoursePaginator _paginator;
        private TableOfContentsViewModel _tableOfContents;
        
        #endregion

        #region Ctor 
        
        public CourseViewModel(string fileName) : base(CourseLoader.GetFromDisk(fileName))
        {
            UpdateChapters();
        }

        public CourseViewModel(Course course) : base(course)
        {
            UpdateChapters();
        }
        
        #endregion

        #region Properties 
        
        public ObservableCollection<CourseLocation> Bookmarks
        {
            get { return _bookmarks; }
            set
            {
                _bookmarks = value;
                RaisePropertyChanged("Bookmarks");
            }
        }

        /// <summary>
        /// A collection of ChapterViewModels that represent each of the Chapter
        /// objects in the ChapterGroup object that this VieWModel wraps
        /// </summary>
        public ChapterViewModelCollection Chapters
        {
            get { return _chapters; }
            set
            {
                _chapters = value;
                RaisePropertyChanged("Chapters");
            }
        }

        public CourseLocation CurrentLocation
        {
            get { return _currentLocation; }
            set
            {
                _currentLocation = value;
                RaisePropertyChanged("CurrentLocation");
            }
        }

        public List<FlowDocument> FlatChapterDocuments
        {
            get
            {
                if (_flatChapterDocuments == null)
                {
                    _flatChapterDocuments = new List<FlowDocument>();

                    foreach (var ch in FlatChapters)
                        _flatChapterDocuments.Add(ch.Document);
                }

                return _flatChapterDocuments;
            }
        }

        /// <summary>
        /// A flattened view of all of the chapters contained within the course.
        /// </summary>
        public ChapterViewModelCollection FlatChapters
        {
            get
            {
                if (_flatChapters == null)
                {
                    _flatChapters = new ChapterViewModelCollection();

                    foreach (var ch in Chapters)
                        AddChapter(ch, _flatChapters);
                }

                return _flatChapters;
            }
        }

        public bool IsFiltered
        {
            get { return _isFiltered; }
            set
            {
                _isFiltered = value;
                RaisePropertyChanged("IsFiltered");
            }
        }

        public bool IsHighlighted
        {
            get { return _isHighlighted; }
            set
            {
                _isHighlighted = value;
                RaisePropertyChanged("IsHighlighted");
            }
        }

        public ICoursePaginator Paginator
        {
            get { return _paginator; }
            set
            {
                if (value == _paginator) return;
                // old value
                if (_paginator != null)
                {
                    _paginator.PaginationCompleted -= _Paginator_PaginationCompleted;
                    _paginator.PaginationStarted -= _Paginator_PaginationStarted;
                }

                _paginator = value;

                // new value
                if (_paginator != null)
                {
                    _paginator.PaginationCompleted += _Paginator_PaginationCompleted;
                    _paginator.PaginationStarted += _Paginator_PaginationStarted;
                }

                GenerateBookmarks();

                RaisePropertyChanged("Paginator");
            }
        }

        public TableOfContentsViewModel TableOfContents
        {
            get { return _tableOfContents ?? (_tableOfContents = new TableOfContentsViewModel(this)); }
            set
            {
                _tableOfContents = value;
                RaisePropertyChanged("TableOfContents");
            }
        }
        
        #endregion

        #region Event Handlers 
        
        private void _Paginator_PaginationCompleted(object sender, RoutedEventArgs e)
        {
            if (Paginator == null) return;
            RefreshPositions();
        }

        private void _Paginator_PaginationStarted(object sender, RoutedEventArgs e)
        {
            ClearPageNumbers();
        }
        
        #endregion Event Handlers 

        #region Public Methods 
        
        public XmlStreamStore GetAnnotationStoreForChapter(ChapterViewModel chapter)
        {
            if (_annotationStores == null)
                _annotationStores = LoadStreams();

            if (_annotationStores != null)
            {
                if (_annotationStores.ContainsKey(chapter))
                {
                    return _annotationStores[chapter];
                }
                var notesFileName = Model.GetChapterNotesFileName(chapter.Model.Index);
                var stream = new FileStream(notesFileName, FileMode.OpenOrCreate);
                var store = new XmlStreamStore(stream) {AutoFlush = true};
                _annotationStores.Add(chapter, store);

                return store;
            }

            return null;
        }

        public ChapterViewModel GetCurrentChapter()
        {
            return FlatChapters.FirstOrDefault(ch => ch.IsCurrent);
        }

        public int GetCurrentChapterIndex()
        {
            var ch = GetCurrentChapter();
            return FlatChapters.IndexOf(ch);
        }

        public void RefreshBookmarks()
        {
            GenerateBookmarks();
        }

        public void SetCurrentChapter(ChapterViewModel chapter)
        {
            foreach (var ch in FlatChapters)
                ch.IsCurrent = ch.Equals(chapter);
        }

        public void SetCurrentChapterIndex(int chapterIndex)
        {
            var index = Math.Min(chapterIndex, FlatChapters.Count - 1);
            var ch = FlatChapters[index];
            SetCurrentChapter(ch);
        }

        public override string ToString()
        {
            return Model.Title;
        }

        public void UpdateContent()
        {
            RaisePropertyChanged("Content");
        }

        public void NotifyUnload()
        {
            // Save user metadata when course gets unloaded
            Model.UserMetadata.SaveToDisk();
        }
        
        #endregion

        #region Private Methods 

        private static void AddChapter(ChapterViewModel chapter, ICollection<ChapterViewModel> flat)
        {
            flat.Add(chapter);
            foreach (var ch in chapter.Chapters)
                AddChapter(ch, flat);
        }

        private void ClearPageNumbers()
        {
            foreach (var b in TableOfContents.Bookmarks)
                b.CurrentPageNumber = null;

            foreach (var ch in FlatChapters)
                ch.CurrentPageNumber = null;
        }

        private void GenerateBookmarks()
        {
            if (TableOfContents == null || Model.UserMetadata == null || Paginator == null) return;
            
            var unsortedBookmarks = new List<CourseLocationViewModel>();

            // generate bookmarks
            foreach (var b in Model.UserMetadata.Bookmarks)
            {
                var bookmark = new CourseLocationViewModel(b, this);
                unsortedBookmarks.Add(bookmark);
            }

            // generate highlights
            foreach (var loc in
                FlatChapters.Select(GetAnnotationStoreForChapter)
                                  .Select(store => store.GetAnnotations())
                                  .SelectMany(annotations => (from a in annotations
                                                                            where a.Cargos.Count((res => res.Name == "Location")) > 0
                                                                            select a.Cargos.First(res => res.Name == "Location")
                                                                            into r where r != null select r.Contents[0])))
            {
                try
                {
                    var bookmark = new CourseLocationViewModel(CourseLocation.FromXml(loc), this)
                                       {IsHighlight = true};
                    unsortedBookmarks.Add(bookmark);
                }
                catch
                {
                    Debug.WriteLine("Unable to parse BookLocation as annotation cargo.");
                }
            }

            // refresh the TableOfContents bookmark collection
            TableOfContents.Bookmarks =
                new ObservableCollection<CourseLocationViewModel>(
                    unsortedBookmarks.OrderBy(b => b.Model.ToDouble()).AsEnumerable());

            if (Paginator.IsPaginationValid)
                RefreshPositions();
        }

        private static Dictionary<ChapterViewModel, XmlStreamStore> LoadStreams()
        {
            // TODO (we may want to convert to memory streams so we can write notes to a single file)
            return new Dictionary<ChapterViewModel, XmlStreamStore>();
        }

        private void RefreshPositions()
        {
            if (Paginator == null)
            {
                ClearPageNumbers();
                return;
            }

            foreach (var b in TableOfContents.Bookmarks)
            {
                int? p = Paginator.BookLocationToPosition(b.Model);
                b.CurrentPageNumber = p == -1 ? null : p;
            }

            foreach (var ch in FlatChapters)
            {
                ch.CurrentPageNumber = Paginator.BookLocationToPosition(new CourseLocation(FlatChapters.IndexOf(ch), 0));
            }
        }

        /// <summary>
        /// Generates ViewModels for each of the ChapterGroups associated with 
        /// the book object that this ViewModel wraps
        /// </summary>
        private void UpdateChapters()
        {
            Chapters = new ChapterViewModelCollection();

            foreach (Chapter ch in Model.Chapters)
            {
                var chm = new ChapterViewModel(ch, this);
                Chapters.Add(chm);
            }
        }

        #endregion
    }

    public class LibraryBookVisual
    {
        #region Properties 
        
        public VerticalAlignment Alignment { get; set; }
        public double FontSize { get; set; }
        public Brush Foreground { get; set; }
        public int MaxTitleLength { get; set; }
        public int MaxWordLength { get; set; }
        public ImageSource Source { get; set; }
        public string TextFontFamily { get; set; }
        public Rect TextLocation { get; set; }
        public Orientation TextOrientation { get; set; }
        
        #endregion Properties 
    }

    public class LibraryBookVisualCollection : ObservableCollection<LibraryBookVisual>
    {
        //
    }

    public interface ICoursePaginator
    {
        //  Properties
        bool IsPaginationValid { get; }

        // Methods
        int BookLocationToPosition(CourseLocation b);
        CourseLocation PositionToBookLocation(int p);
        void GotoLocation(CourseLocation b);

        // Events
        event RoutedEventHandler PaginationCompleted;
        event RoutedEventHandler PaginationStarted;
    }
}